More and more tests (cargo upload)
authorAlex Crichton <alex@alexcrichton.com>
Tue, 9 Sep 2014 14:23:09 +0000 (07:23 -0700)
committerAlex Crichton <alex@alexcrichton.com>
Tue, 16 Sep 2014 19:05:21 +0000 (12:05 -0700)
src/cargo/ops/cargo_upload.rs
src/cargo/sources/git/mod.rs
src/cargo/sources/git/utils.rs
src/cargo/sources/registry.rs
tests/support/git.rs [new file with mode: 0644]
tests/support/mod.rs
tests/test_cargo_package.rs
tests/test_cargo_registry.rs
tests/test_cargo_upload.rs [new file with mode: 0644]
tests/tests.rs

index 36f6b6a59582bab1f06d1b764425e85840076c1d..c6df621bc4b613385950dac8c8f4d4c5d5028d0c 100644 (file)
@@ -8,7 +8,7 @@ use curl::http;
 use git2;
 
 use core::source::Source;
-use core::{Package, MultiShell, SourceId};
+use core::{Package, MultiShell, SourceId, RegistryKind};
 use ops;
 use sources::{PathSource, RegistrySource};
 use util::config;
@@ -34,6 +34,16 @@ pub fn upload(manifest_path: &Path,
         human("no upload token found, please run `cargo login`")
     }));
     let host = host.unwrap_or(try!(RegistrySource::url()).to_string());
+    let host = try!(host.as_slice().to_url().map_err(human));
+    let upload = {
+        let sid = SourceId::new(RegistryKind, host.clone());
+        let mut config = try!(Config::new(shell, None, None));
+        let mut src = RegistrySource::new(&sid, &mut config);
+        try!(src.update().chain_error(|| {
+            human(format!("Failed to update registry {}", host))
+        }));
+        (try!(src.config())).upload
+    };
 
     // First, prepare a tarball
     let tarball = try!(ops::package(manifest_path, shell));
@@ -42,8 +52,8 @@ pub fn upload(manifest_path: &Path,
     // Upload said tarball to the specified destination
     try!(shell.status("Uploading", pkg.get_package_id().to_string()));
     try!(transmit(&pkg, tarball, token.as_slice(),
-                  host.as_slice()).chain_error(|| {
-        human(format!("failed to upload package to registry: {}", host))
+                  upload.as_slice()).chain_error(|| {
+        human(format!("failed to upload package to registry: {}", upload))
     }));
 
     Ok(())
@@ -55,9 +65,8 @@ fn transmit(pkg: &Package, mut tarball: File,
     let url = try!(host.to_url().map_err(human));
     let registry_src = SourceId::for_registry(&url);
 
-    let url = format!("{}/packages/new", host.trim_right_chars('/'));
     let mut handle = try!(http_handle());
-    let mut req = handle.post(url.as_slice(), &mut tarball)
+    let mut req = handle.put(host, &mut tarball)
                         .content_length(stat.size as uint)
                         .content_type("application/x-tar")
                         .header("Content-Encoding", "x-gzip")
@@ -83,6 +92,7 @@ fn transmit(pkg: &Package, mut tarball: File,
 
     let response = try!(req.exec());
 
+    if response.get_code() == 0 { return Ok(()) } // file upload url
     if response.get_code() != 200 {
         return Err(internal(format!("failed to get a 200 response: {}",
                                     response)))
index 8b1ba4977e590825fd0a9ccb68afe64120c884c0..0ef4db4d6681bc199b7c25d8fb980832c57977c9 100644 (file)
@@ -1,4 +1,4 @@
-pub use self::utils::{GitRemote, GitDatabase, GitCheckout, GitRevision};
+pub use self::utils::{GitRemote, GitDatabase, GitCheckout, GitRevision, fetch};
 pub use self::source::{GitSource, canonicalize_url};
 mod utils;
 mod source;
index 03a2ab0e006e6537c9ac071f9e31824f0948053c..693b96e9d830a772ec7994db17b4430bba41aa5c 100644 (file)
@@ -174,7 +174,8 @@ impl GitRemote {
     fn fetch_into(&self, dst: &git2::Repository) -> CargoResult<()> {
         // Create a local anonymous remote in the repository to fetch the url
         let url = self.url.to_string();
-        fetch(dst, url.as_slice())
+        let refspec = "refs/heads/*:refs/heads/*";
+        fetch(dst, url.as_slice(), refspec)
     }
 
     fn clone_into(&self, dst: &Path) -> CargoResult<git2::Repository> {
@@ -336,7 +337,8 @@ impl<'a> GitCheckout<'a> {
                 };
 
                 // Fetch data from origin and reset to the head commit
-                try!(fetch(&repo, url).chain_error(|| {
+                let refspec = "refs/heads/*:refs/heads/*";
+                try!(fetch(&repo, url, refspec).chain_error(|| {
                     internal(format!("failed to fetch submodule `{}` from {}",
                                      child.name().unwrap_or(""), url))
                 }));
@@ -399,9 +401,9 @@ fn with_authentication<T>(url: &str,
     }
 }
 
-fn fetch(repo: &git2::Repository, url: &str) -> CargoResult<()> {
+pub fn fetch(repo: &git2::Repository, url: &str,
+             refspec: &str) -> CargoResult<()> {
     // Create a local anonymous remote in the repository to fetch the url
-    let refspec = "refs/heads/*:refs/heads/*";
 
     with_authentication(url, &try!(repo.config()), |f| {
         let mut cb = git2::RemoteCallbacks::new()
index 1a46c97be0d4c8391c6a0ac21d22a262b1d781de..82abb06bae1ad5f0e8db275c1eb4a0c8438feda1 100644 (file)
@@ -13,7 +13,7 @@ use url::Url;
 
 use core::{Source, SourceId, PackageId, Package, Summary, Registry};
 use core::Dependency;
-use sources::PathSource;
+use sources::{PathSource, git};
 use util::{CargoResult, Config, internal, ChainError, ToUrl, human};
 use util::{hex, Require, Sha256};
 use ops;
@@ -32,8 +32,9 @@ pub struct RegistrySource<'a, 'b:'a> {
 }
 
 #[deriving(Decodable)]
-struct RegistryConfig {
-    dl_url: String,
+pub struct RegistryConfig {
+    pub dl: String,
+    pub upload: String,
 }
 
 #[deriving(Decodable)]
@@ -75,7 +76,7 @@ impl<'a, 'b> RegistrySource<'a, 'b> {
     /// Decode the configuration stored within the registry.
     ///
     /// This requires that the index has been at least checked out.
-    fn config(&self) -> CargoResult<RegistryConfig> {
+    pub fn config(&self) -> CargoResult<RegistryConfig> {
         let mut f = try!(File::open(&self.checkout_path.join("config.json")));
         let contents = try!(f.read_to_string());
         let config = try!(json::decode(contents.as_slice()));
@@ -122,7 +123,7 @@ impl<'a, 'b> RegistrySource<'a, 'b> {
                 self.handle.as_mut().unwrap()
             }
         };
-        // TODO: don't download into memory
+        // TODO: don't download into memory (curl-rust doesn't expose it)
         let resp = try!(handle.get(url.to_string()).exec());
         if resp.get_code() != 200 && resp.get_code() != 0 {
             return Err(internal(format!("Failed to get 200 reponse from {}\n{}",
@@ -162,7 +163,7 @@ impl<'a, 'b> RegistrySource<'a, 'b> {
         try!(fs::mkdir_recursive(&dst.dir_path(), io::UserDir));
         let f = try!(File::open(&tarball));
         let mut gz = try!(GzDecoder::new(f));
-        // TODO: don't read into memory
+        // TODO: don't read into memory (Archive requires Seek)
         let mem = try!(gz.read_to_end());
         let tar = Archive::new(MemReader::new(mem));
         for file in try!(tar.files()) {
@@ -229,10 +230,7 @@ impl<'a, 'b> Source for RegistrySource<'a, 'b> {
         // git fetch origin
         let url = self.source_id.get_url().to_string();
         let refspec = "refs/heads/*:refs/remotes/origin/*";
-        let mut remote = try!(repo.remote_create_anonymous(url.as_slice(),
-                                                           refspec));
-        log!(5, "[{}] fetching {}", self.source_id, url);
-        try!(remote.fetch(None, None).chain_error(|| {
+        try!(git::fetch(&repo, url.as_slice(), refspec).chain_error(|| {
             internal(format!("failed to fetch `{}`", url))
         }));
 
@@ -247,7 +245,7 @@ impl<'a, 'b> Source for RegistrySource<'a, 'b> {
 
     fn download(&mut self, packages: &[PackageId]) -> CargoResult<()> {
         let config = try!(self.config());
-        let url = try!(config.dl_url.as_slice().to_url().map_err(internal));
+        let url = try!(config.dl.as_slice().to_url().map_err(internal));
         for package in packages.iter() {
             if self.source_id != *package.get_source_id() { continue }
 
diff --git a/tests/support/git.rs b/tests/support/git.rs
new file mode 100644 (file)
index 0000000..e2c9d23
--- /dev/null
@@ -0,0 +1,50 @@
+use std::io::{mod, fs, File};
+
+use git2;
+
+pub struct RepoBuilder {
+    repo: git2::Repository,
+    files: Vec<Path>,
+}
+
+pub fn repo(p: &Path) -> RepoBuilder { RepoBuilder::init(p) }
+
+impl RepoBuilder {
+    pub fn init(p: &Path) -> RepoBuilder {
+        fs::mkdir_recursive(&p.dir_path(), io::UserDir).unwrap();
+        let repo = git2::Repository::init(p).unwrap();
+        {
+            let mut config = repo.config().unwrap();
+            config.set_str("user.name", "name").unwrap();
+            config.set_str("user.email", "email").unwrap();
+        }
+        RepoBuilder { repo: repo, files: Vec::new() }
+    }
+
+    pub fn file<T: Str>(self, path: &str, contents: T) -> RepoBuilder {
+        let mut me = self.nocommit_file(path, contents);
+        me.files.push(Path::new(path));
+        me
+    }
+
+    pub fn nocommit_file<T: Str>(self, path: &str,
+                                 contents: T) -> RepoBuilder {
+        let dst = self.repo.path().dir_path().join(path);
+        fs::mkdir_recursive(&dst.dir_path(), io::UserDir).unwrap();
+        File::create(&dst).write_str(contents.as_slice()).unwrap();
+        self
+    }
+
+    pub fn build(&self) {
+        let mut index = self.repo.index().unwrap();
+        for file in self.files.iter() {
+            index.add_path(file).unwrap();
+        }
+        index.write().unwrap();
+        let id = index.write_tree().unwrap();
+        let tree = git2::Tree::lookup(&self.repo, id).unwrap();
+        let sig = git2::Signature::default(&self.repo).unwrap();
+        git2::Commit::new(&self.repo, Some("HEAD"), &sig, &sig,
+                          "Initial commit", &tree, []).unwrap();
+    }
+}
index 8d177ef266a3fec8d1b7bc0e3eef40a80df0fa23..5eafd281c45179949c8eb75810cbba2592da3760 100644 (file)
@@ -14,6 +14,7 @@ use cargo::util::ProcessError;
 use support::paths::PathExt;
 
 pub mod paths;
+pub mod git;
 
 /*
  *
@@ -512,3 +513,4 @@ pub static UPDATING:    &'static str = "    Updating";
 pub static DOCTEST:     &'static str = "   Doc-tests";
 pub static PACKAGING:   &'static str = "   Packaging";
 pub static DOWNLOADING: &'static str = " Downloading";
+pub static UPLOADING:   &'static str = "   Uploading";
index 5761916e0c600675501c1c72915112e218eccdfc..882113cb45f9bcb33749ed784cb3fbf5a93de2d9 100644 (file)
@@ -1,8 +1,8 @@
-extern crate tar;
-extern crate flate2;
-
 use std::io::{File, MemReader};
 
+use tar::Archive;
+use flate2::reader::GzDecoder;
+
 use support::{project, execs, cargo_dir, ResultTest};
 use support::{PACKAGING};
 use hamcrest::{assert_that, existing_file};
@@ -35,9 +35,9 @@ test!(simple {
                 execs().with_status(0).with_stdout(""));
 
     let f = File::open(&p.root().join("foo-0.0.1.tar.gz")).assert();
-    let mut rdr = flate2::reader::GzDecoder::new(f);
+    let mut rdr = GzDecoder::new(f);
     let contents = rdr.read_to_end().assert();
-    let ar = tar::Archive::new(MemReader::new(contents));
+    let ar = Archive::new(MemReader::new(contents));
     for f in ar.files().assert() {
         let f = f.assert();
         match f.filename().unwrap() {
index 6c40e415862c159c8a19382d9f6b0ee0caa75f3f..4ecbf476839a7aa58b2be1544df57c3399057e15 100644 (file)
@@ -6,6 +6,7 @@ use serialize::hex::ToHex;
 use support::{ResultTest, project, execs, cargo_dir};
 use support::{UPDATING, DOWNLOADING, COMPILING};
 use support::paths;
+use support::git::repo;
 use cargo::util::Sha256;
 
 use hamcrest::assert_that;
@@ -30,15 +31,6 @@ fn setup() {
             token = "api-token"
     "#, reg = registry()).as_slice()).assert();
 
-    fs::mkdir(&registry_path(), io::UserDir).assert();
-
-    // Init a new registry
-    let repo = git2::Repository::init(&registry_path()).unwrap();
-    let mut config = repo.config().unwrap();
-    config.set_str("user.name", "name").unwrap();
-    config.set_str("user.email", "email").unwrap();
-    let mut index = repo.index().unwrap();
-
     // Prepare the "to download" artifacts
     let foo = include_bin!("fixtures/foo-0.0.1.tar.gz");
     let bar = include_bin!("fixtures/bar-0.0.1.tar.gz");
@@ -48,39 +40,26 @@ fn setup() {
     dl("pkg/bad-cksum/bad-cksum-0.0.1.tar.gz", foo);
     let notyet = dl("pkg/notyet/notyet-0.0.1.tar.gz", notyet);
 
-    // Prepare the registry's git repo
-    file(&mut index, "config.json", format!(r#"
-        {{"dl_url":"{}"}}
-    "#, dl_url()).as_slice());
-    file(&mut index, "fo/oX/foo",
-         format!(r#"{{"name":"foo","vers":"0.0.1","deps":[],"cksum":"{}"}}"#,
-                 foo_cksum).as_slice());
-    file(&mut index, "ba/rX/bar",
-         format!(r#"{{"name":"bar","vers":"0.0.1","deps":["foo|>=0.0.0"],"cksum":"{}"}}"#,
-                 bar_cksum).as_slice());
-    file(&mut index, "ba/d-/bad-cksum",
-         format!(r#"{{"name":"bad-cksum","vers":"0.0.1","deps":[],"cksum":"{}"}}"#,
-                 bar_cksum).as_slice());
-    file(&mut index, "no/ty/notyet",
-         format!(r#"{{"name":"notyet","vers":"0.0.1","deps":[],"cksum":"{}"}}"#,
-                 notyet).as_slice());
-    index.remove_path(&Path::new("no/ty/notyet")).unwrap();
-
-    // Commit!
-    index.write().unwrap();
-    let id = index.write_tree().unwrap();
-    let tree = git2::Tree::lookup(&repo, id).unwrap();
-    let sig = git2::Signature::default(&repo).unwrap();
-    git2::Commit::new(&repo, Some("HEAD"), &sig, &sig,
-                      "Initial commit", &tree, []).unwrap();
-
-    fn file(index: &mut git2::Index, path: &str, contents: &str) {
-        let dst = index.path().unwrap().dir_path().dir_path().join(path);
-        fs::mkdir_recursive(&dst.dir_path(), io::UserDir).assert();
-        File::create(&dst).write_str(contents).unwrap();
-        index.add_path(&Path::new(path)).unwrap();
+    // Init a new registry
+    repo(&registry_path())
+        .file("config.json", format!(r#"
+            {{"dl":"{}","upload":""}}
+        "#, dl_url()).as_slice())
+        .file("fo/oX/foo", pkg("foo", "0.0.1", [], &foo_cksum))
+        .file("ba/rX/bar", pkg("bar", "0.0.1", ["foo|>=0.0.0"], &bar_cksum))
+        .file("ba/d-/bad-cksum", pkg("bad-cksum", "0.0.1", [], &bar_cksum))
+        .nocommit_file("no/ty/notyet", pkg("notyet", "0.0.1", [], &notyet))
+        .build();
+
+    fn pkg(name: &str, vers: &str, deps: &[&str], cksum: &String) -> String {
+        let deps: Vec<String> = deps.iter().map(|s| {
+            format!("\"{}\"", s)
+        }).collect();
+        let deps = deps.connect(",");
+
+        format!(r#"{{"name":"{}","vers":"{}","deps":[{}],"cksum":"{}"}}"#,
+                name, vers, deps, cksum)
     }
-
     fn dl(path: &str, contents: &[u8]) -> String {
         let dst = dl_path().join(path);
         fs::mkdir_recursive(&dst.dir_path(), io::UserDir).assert();
@@ -114,6 +93,17 @@ test!(simple {
         compiling = COMPILING,
         dir = p.url(),
         reg = registry()).as_slice()));
+
+    // Don't download a second time
+    assert_that(p.cargo_process("build"),
+                execs().with_status(0).with_stdout(format!("\
+{updating} registry `{reg}`
+[..] foo v0.0.1 (the package registry)
+[..] foo v0.0.1 ({dir})
+",
+        updating = UPDATING,
+        dir = p.url(),
+        reg = registry()).as_slice()));
 })
 
 test!(deps {
diff --git a/tests/test_cargo_upload.rs b/tests/test_cargo_upload.rs
new file mode 100644 (file)
index 0000000..a450960
--- /dev/null
@@ -0,0 +1,91 @@
+use std::io::{mod, fs, File, MemReader};
+
+use flate2::reader::GzDecoder;
+use tar::Archive;
+use url::Url;
+
+use support::{ResultTest, project, execs};
+use support::{UPDATING, PACKAGING, UPLOADING};
+use support::paths;
+use support::git::repo;
+
+use hamcrest::assert_that;
+
+fn registry_path() -> Path { paths::root().join("registry") }
+fn registry() -> Url { Url::from_file_path(&registry_path()).unwrap() }
+fn upload_path() -> Path { paths::root().join("upload") }
+fn upload() -> Url { Url::from_file_path(&upload_path()).unwrap() }
+
+fn setup() {
+    let config = paths::root().join(".cargo/config");
+    fs::mkdir_recursive(&config.dir_path(), io::UserDir).assert();
+    File::create(&config).write_str(format!(r#"
+        [registry]
+            host = "{reg}"
+            token = "api-token"
+    "#, reg = registry()).as_slice()).assert();
+
+    repo(&registry_path())
+        .file("config.json", format!(r#"{{
+            "dl": "",
+            "upload": "{}"
+        }}"#, upload()))
+        .build();
+}
+
+test!(simple {
+    let p = project("foo")
+        .file("Cargo.toml", r#"
+            [project]
+            name = "foo"
+            version = "0.0.1"
+            authors = []
+        "#)
+        .file("src/main.rs", "fn main() {}");
+
+    assert_that(p.cargo_process("upload"),
+                execs().with_status(0).with_stdout(format!("\
+{updating} registry `{reg}`
+{packaging} foo v0.0.1 ({dir})
+{uploading} foo v0.0.1 ({dir})
+",
+        updating = UPDATING,
+        uploading = UPLOADING,
+        packaging = PACKAGING,
+        dir = p.url(),
+        reg = registry()).as_slice()));
+
+    let mut rdr = GzDecoder::new(File::open(&upload_path()).unwrap()).unwrap();
+    assert_eq!(rdr.header().filename(), Some(b"foo-0.0.1.tar.gz"));
+    let inner = MemReader::new(rdr.read_to_end().unwrap());
+    let ar = Archive::new(inner);
+    for file in ar.files().unwrap() {
+        let file = file.unwrap();
+        assert!(file.filename() == Some("foo-0.0.1/Cargo.toml") ||
+                file.filename() == Some("foo-0.0.1/src/main.rs"),
+                "bad filename: {}", file.filename());
+    }
+})
+
+test!(git_deps {
+    let p = project("foo")
+        .file("Cargo.toml", r#"
+            [project]
+            name = "foo"
+            version = "0.0.1"
+            authors = []
+
+            [dependencies.foo]
+            git = "git://path/to/nowhere"
+        "#)
+        .file("src/main.rs", "fn main() {}");
+
+    assert_that(p.cargo_process("upload").arg("-v"),
+                execs().with_status(101).with_stderr("\
+failed to upload package to registry: [..]
+
+Caused by:
+  All dependencies must come from the same registry.
+Dependency `foo` comes from git://path/to/nowhere instead
+"));
+})
index 56133c420cdfb0b00091701e77b44f3253b1e1b5..7ece4ff5f20ba34f7382c34cfcbfb4f117672ef7 100644 (file)
@@ -2,9 +2,11 @@
 #![feature(phase)]
 
 extern crate cargo;
+extern crate flate2;
 extern crate git2;
 extern crate hamcrest;
 extern crate serialize;
+extern crate tar;
 extern crate term;
 extern crate url;
 
@@ -43,3 +45,4 @@ mod test_cargo_profiles;
 mod test_cargo_package;
 mod test_cargo_build_auth;
 mod test_cargo_registry;
+mod test_cargo_upload;